package mspbots.qbd.online.core.balancesheet;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.log4j.Log4j2;
import mspbots.qbd.common.*;
import mspbots.qbd.online.core.AbstractToolsUtil;
import mspbots.qbd.online.core.BaseSyncService;
import mspbots.qbd.online.oauth.TokenState;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Locale;

/**
 * @author Jay.Yang
 */
@Log4j2
@Service
public class BalanceSheetServiceImpl extends AbstractToolsUtil implements BaseSyncService {

    private Mono<JsonNode> get(LocalDate syncStartDate) {
        QueryParams params = QueryParams.withDefault("");
        params.add("start_date", syncStartDate.with(TemporalAdjusters.firstDayOfMonth())
                .format(DateTimeFormatter.ISO_DATE));
        params.add("end_date", syncStartDate.with(TemporalAdjusters.lastDayOfMonth())
                .format(DateTimeFormatter.ISO_DATE));
        return this.onlineClient.get("/company/{realmId}/reports/BalanceSheet", params);
    }

    @Override
    public Mono<Void> syncData(TokenState tokenState) {
        String redisKey = RedisKeys.BALANCE_SHEET_ONLINE_SYNC_ANCHOR_REDIS_KEY + tokenState.getTenantId();
        ResultSync resultSync = ResultSync.withDefault("BALANCE-SHEET-SYNC");
        return super.stringRedisTemplate.opsForValue().get(redisKey)
                .map(str -> LocalDate.parse(str, DateTimeFormatter.ISO_DATE))
                .defaultIfEmpty(LocalDate.now().minusYears(20))
                .flatMapMany(startDate -> {
                    long count = ChronoUnit.MONTHS.between(startDate, LocalDate.now());
                    return Flux.range(0, Integer.parseInt(String.valueOf(count)))
                            .map(startDate::plusMonths);
                })
                .delayElements(Duration.ofSeconds(1))
                .doOnNext(syncStartDate -> this.get(syncStartDate)
                        .subscriberContext(ctx -> ctx.put("tokenState", tokenState))
                        .subscriberContext(ctx -> ctx.put("resultSync", resultSync))
                        .subscribe(result -> {
                            ObjectNode objectNode = result.deepCopy();
                            objectNode.put("tenantId", tokenState.getTenantId());
                            objectNode.put("timePeriod", syncStartDate.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH) +
                                    " " + syncStartDate.getYear());
                            this.sendEntity(RabbitKeys.BALANCE_SHEET_ONLINE_SYNC_QUEUE_KEY, ResultMsg.builder()
                                    .requestId(resultSync.getRequestId()).id(syncStartDate.toEpochDay())
                                    .action("none").entity(objectNode).tenantId(tokenState.getTenantId())
                                    .syncDate(LocalDateTime.now()).build());
                        }))
                .doOnNext(startDate -> super.stringRedisTemplate.opsForValue()
                        .set(redisKey, startDate.format(DateTimeFormatter.ISO_DATE), Duration.ofDays(7))
                        .subscribe())
                .then();
    }


}
